package com.cloud.pay.payment;

import com.cloud.common.util.JsonUtils;
import com.cloud.pay.config.AppConfig;
import com.cloud.pay.constant.PayChannelEnum;
import com.cloud.pay.constant.TradeTypeEnum;
import com.cloud.pay.entity.PayConfig;
import com.cloud.pay.entity.Payment;
import com.cloud.pay.entity.Refund;
import com.egzosn.pay.common.bean.CertStoreType;
import com.egzosn.pay.common.bean.PayOrder;
import com.egzosn.pay.common.bean.PayOutMessage;
import com.egzosn.pay.common.bean.RefundOrder;
import com.egzosn.pay.common.http.HttpConfigStorage;
import com.egzosn.pay.common.util.sign.SignUtils;
import com.egzosn.pay.wx.api.WxPayConfigStorage;
import com.egzosn.pay.wx.api.WxPayService;
import com.egzosn.pay.wx.bean.WxTransactionType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class WxPayAdapter implements PayAdapter {

    private static final String CERT_FILE_SUFFIX = "_apiclient_cert.p12";

    private Map<String, WxPayService> wxPayServiceMap = new HashMap<>();
    private Map<String, WxPayService> sslWxPayServiceMap = new HashMap<>();
    private Map<String, String> configMap = new HashMap<>();

    @Autowired
    private WxPayBackHandler wxPayBackHandler;

    @Autowired
    private AppConfig appConfig;

    @Override
    public String getQrcode(Payment payment) {
        WxPayService payService = wxPayServiceMap.get(payment.getAppId());
        if (payService == null) {
            log.info("no appId {}", payment.getAppId());
            return null;
        }
        PayOrder payOrder = new PayOrder(payment.getPayTitle(), "", new BigDecimal(payment.getPayMoney() / 100), payment.getOrderNo(), WxTransactionType.NATIVE);
        return payService.getQrPay(payOrder);
    }

    @Override
    public String orderInfo(Payment payment) {
        WxPayService payService = wxPayServiceMap.get(payment.getAppId());
        if (payService == null) {
            log.info("no appId {}", payment.getAppId());
            return null;
        }
        WxTransactionType transactionType = TradeTypeEnum.of(payment.getTradeType()) == TradeTypeEnum.APP ? WxTransactionType.APP : WxTransactionType.JSAPI;
        PayOrder payOrder = new PayOrder(payment.getPayTitle(), "", new BigDecimal(payment.getPayMoney() / 100), payment.getOrderNo(), transactionType);
        return JsonUtils.toJSONString(payService.orderInfo(payOrder));
    }

    @Override
    public boolean refund(Refund refund) {
        WxPayService payService = sslWxPayServiceMap.get(refund.getAppId());
        if (payService == null) {
            log.info("no appId {}", refund.getAppId());
            return false;
        }
        RefundOrder refundOrder = new RefundOrder(refund.getRefundNo(), refund.getTradeNo(), new BigDecimal(refund.getRefundMoney() / 100));
        refundOrder.setTotalAmount(refundOrder.getRefundAmount());
        Map result = payService.refund(refundOrder);
        if (result.get(WxPayService.RETURN_CODE).equals(WxPayService.SUCCESS)) {
            return true;
        }
        return false;
    }

    @Override
    public PayOutMessage payBack(String appId, Map<String, String[]> parameterMap, InputStream input) {
        WxPayService payService = wxPayServiceMap.get(appId);
        if (payService == null) {
            log.info("no appId {}", appId);
            return null;
        }
        return payService.payBack(parameterMap, input);
    }

    @Override
    public void loadConfig(List<PayConfig> list) {
        for (PayConfig payConfig : list) {
            if (PayChannelEnum.of(payConfig.getChannel()) != PayChannelEnum.WECHAT_PAY) {
                continue;
            }
            String configJson = JsonUtils.toJSONString(payConfig);
            String configs = configMap.get(payConfig.getAppId());
            if (configs == null || !configs.equals(configJson)) {
                wxPayServiceMap.put(payConfig.getAppId(), createWxPayService(payConfig));
                sslWxPayServiceMap.put(payConfig.getAppId(), createSslWxPayService(payConfig));
                configMap.put(payConfig.getAppId(), configJson);
            }
        }
    }

    /**
     * 创建微信支付配置
     * @param payConfig
     * @return
     */
    private WxPayConfigStorage createWxPayConfigStorage(PayConfig payConfig) {
        WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage();
        wxPayConfigStorage.setAppid(payConfig.getAppId());
        wxPayConfigStorage.setMchId(payConfig.getAccountId());
        wxPayConfigStorage.setSecretKey(payConfig.getApiSecretkey());
        wxPayConfigStorage.setSignType(SignUtils.MD5.name());
        wxPayConfigStorage.setInputCharset("utf-8");
        return wxPayConfigStorage;
    }

    /**
     * 创建微信支付服务
     *
     * @param payConfig
     */
    private WxPayService createWxPayService(PayConfig payConfig) {
        WxPayConfigStorage wxPayConfigStorage = createWxPayConfigStorage(payConfig);
        wxPayConfigStorage.setNotifyUrl(appConfig.getWxPayNotifyUrl().replace("appId", payConfig.getAppId()));

        log.info("createWxPayService appId >>>" + wxPayConfigStorage.getAppid());

        // 请求连接池配置
        HttpConfigStorage httpConfigStorage = new HttpConfigStorage();
        httpConfigStorage.setMaxTotal(20);
        httpConfigStorage.setDefaultMaxPerRoute(10);

        WxPayService payService = new WxPayService(wxPayConfigStorage);
        payService.setRequestTemplateConfigStorage(httpConfigStorage);
        payService.setPayMessageHandler(wxPayBackHandler);
        return payService;
    }

    /**
     * 创建SSL微信支付服务，退款需要用到
     *
     * @param payConfig
     */
    private WxPayService createSslWxPayService(PayConfig payConfig) {
        WxPayConfigStorage wxPayConfigStorage = createWxPayConfigStorage(payConfig);

        log.info("createSslWxPayService appId >>>" + wxPayConfigStorage.getAppid());

        // SSL请求连接池配置
        HttpConfigStorage sslHttpConfigStorage = new HttpConfigStorage();
        sslHttpConfigStorage.setMaxTotal(5);
        sslHttpConfigStorage.setDefaultMaxPerRoute(2);
        sslHttpConfigStorage.setKeystore(appConfig.getWxPayCertPath() + "/" + wxPayConfigStorage.getMchId() + CERT_FILE_SUFFIX);
        sslHttpConfigStorage.setStorePassword(wxPayConfigStorage.getMchId());
        sslHttpConfigStorage.setCertStoreType(CertStoreType.PATH);

        WxPayService payService = new WxPayService(wxPayConfigStorage);
        payService.setRequestTemplateConfigStorage(sslHttpConfigStorage);
        return payService;
    }
}
